package com.iisigroup.log.statistics;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.GZIPInputStream;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.apache.commons.lang.time.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.iisigroup.log.model.LogRecordV1;
import com.iisigroup.log.statistics.internal.LogRecordV1SortByTime;
import com.iisigroup.log.statistics.internal.LogicProcess;
import com.iisigroup.log.statistics.internal.LogicProcessByThread;

public class FileProcessV2 extends FileProcess{
    private static Logger logger = LoggerFactory.getLogger(FileProcessV2.class);
    private final   String pattern="(1[0-2]|0[1-9])\\/(3[01]|[12][0-9]|0?[1-9])\\-(2[0-3]|[01]?[0-9]):([0-5]?[0-9]):([0-5]?[0-9]).\\d{3}";
    private final  String patternDateTime5="^(1[0-2]|0[1-9])\\/(3[01]|[12][0-9]|0?[1-9])\\-(2[0-3]|[01]?[0-9]):([0-5]?[0-9]):([0-5]?[0-9]).*\\d?\\)$";
    private final  String patternDateTime6="^(1[0-2]|0[1-9])\\/(3[01]|[12][0-9]|0?[1-9])\\-(2[0-3]|[01]?[0-9]):([0-5]?[0-9]):([0-5]?[0-9]).*";
    private final  String patternDateTime7=".*\\d?\\)$";
    
   
    public void extractByRegEXpr(final File... srcFiles){}
    
    public void category(final File dir,final File... srcFiles){
	if(ArrayUtils.isNotEmpty(srcFiles)){
	    final Charset cs=Charset.forName("UTF-8");
	    final   ConcurrentMap<String, List<LogRecordV1>> data = reagLogsByAccount(srcFiles);
	    final Set<String> accounts = data.keySet();
	    for(String account :accounts){
		List<LogRecordV1> unitData = data.get(account);
		String dirPath =  dir.getAbsolutePath();
		try {
		    if (!dir.exists()) {
			Files.createDirectories(dir.toPath());
		    }
		    final Path outPath = Paths.get(dirPath, account);
		    List<String> lines = new ArrayList<String>(unitData.size());
		    for (LogRecordV1 unitSrc : unitData) {
			lines.add(unitSrc.getSrc());
		    }

		    Files.write(outPath, lines, cs, StandardOpenOption.CREATE_NEW);
		    data.remove(account);
		} catch (Exception e) {
		    e.printStackTrace();
		}
		
	    }
	}
    }
    public List<LogRecordV1> readLogs(final File... srcFiles){
	long logicProcessStartTime = System.nanoTime();
	List<LogRecordV1> result = new ArrayList<LogRecordV1>();
	if(ArrayUtils.isNotEmpty(srcFiles)){
	    for(File file : srcFiles  ){
		logger.debug("file:  {}",file.getName());
		final	List<String> lineList = this.readLines(file);
		processConvert(result, lineList);
	    }
	}
	logger.debug("Time (seconds) taken  by reagLogs is  {}", String.valueOf((System.nanoTime() - logicProcessStartTime) / 1.0e9));
	return result;
    }
    
    
    public List<LogRecordV1> readLogsV2(final File... srcFiles){
   	long logicProcessStartTime = System.nanoTime();
   	List<LogRecordV1> result = new ArrayList<LogRecordV1>();
   	
   	if(ArrayUtils.isNotEmpty(srcFiles)){
   	    for(File srcFile : srcFiles  ){
   		BufferedReader br =null;
   		try {
   		    if (srcFile.exists() && srcFile.getName().contains(".gz")) {
   			final Path srcPath = srcFile.toPath();

   			final GZIPInputStream gzip = new GZIPInputStream(Files.newInputStream(srcPath, StandardOpenOption.READ));
   			br = new BufferedReader(new InputStreamReader(gzip));

   		    } else {
   			br = new BufferedReader(new FileReader(srcFile));
   		    }
   		    StringBuffer sbf = null;
   		    while (br.ready()) {
   			String line = br.readLine();
   			if (line.matches(patternDateTime5)) {
   			   final LogRecordV1 unit = super.convert(line);
   			   result.add(unit);
   			}else{
   			    if(line.matches(patternDateTime6)){
   				sbf = new StringBuffer();
   				sbf.append(line).append("\n");
   			    }else  if(line.matches(patternDateTime7)){
   				sbf.append(line).append("\n");
   				final LogRecordV1 unit = super.convert(sbf.toString());
   				 result.add(unit);
   			    }else{
   				sbf.append(line).append("\n");
   			    }
   			}
   		    }
   		} catch (IOException e) {
   		    e.printStackTrace();
   		} finally {
   		    if (br != null) {
   			try {
   			    br.close();
   			} catch (IOException e) {
   			    e.printStackTrace();
   			}
   		    }
   		}
   	    }
   	}
   	logger.debug("Time (seconds) taken  by reagLogs is  {}", String.valueOf((System.nanoTime() - logicProcessStartTime) / 1.0e9));
   	return result;
       }
    @Deprecated
    public void  reagLogsByThread(final File srcFile){

	long logicProcessStartTime = System.nanoTime();
	 
	BufferedReader br =null;
	try {
	    if (srcFile.exists() && srcFile.getName().contains(".gz")) {
		final Path srcPath = srcFile.toPath();

		final GZIPInputStream gzip = new GZIPInputStream(Files.newInputStream(srcPath, StandardOpenOption.READ));
		br = new BufferedReader(new InputStreamReader(gzip));
	    } else {
		br = new BufferedReader(new FileReader(srcFile));
	    }
	    StringBuffer sbf = null;
	    while (br.ready()) {
		String line = br.readLine();
		if (line.matches(patternDateTime5)) {
		   final LogRecordV1 unit = super.convert(line);
		   LogicProcessByThread logic =new LogicProcessByThread(srcFile);
		   logic.process(unit);
		}else{
		    if(line.matches(patternDateTime6)){
			sbf = new StringBuffer();
			sbf.append(line).append("\n");
		    }else  if(line.matches(patternDateTime7)){
			sbf.append(line).append("\n");
			final LogRecordV1 unit = super.convert(sbf.toString());
			LogicProcessByThread logic = new LogicProcessByThread(srcFile);
			logic.process(unit);
		    }else{
			sbf.append(line).append("\n");
		    }
		}
	    }
	} catch (IOException e) {
	    e.printStackTrace();
	} finally {
	    if (br != null) {
		try {
		    br.close();
		} catch (IOException e) {
		    e.printStackTrace();
		}
	    }
	}
	 
	
	logger.debug("Time (seconds) taken  by reagLogsByThread is  {}", String.valueOf((System.nanoTime() - logicProcessStartTime) / 1.0e9));
	
    }
    public List<LogRecordV1> readLogsBySortTime(final File srcFile){
	long logicProcessStartTime = System.nanoTime();
	List<LogRecordV1> result = new ArrayList<LogRecordV1>();
	BufferedReader br =null;
	try {
	    if (srcFile.exists() && srcFile.getName().contains(".gz")) {
		final Path srcPath = srcFile.toPath();

		final GZIPInputStream gzip = new GZIPInputStream(Files.newInputStream(srcPath, StandardOpenOption.READ));
		br = new BufferedReader(new InputStreamReader(gzip));
	    } else {
		br = new BufferedReader(new FileReader(srcFile));
	    }
	    StringBuffer sbf = null;
	    while (br.ready()) {
		String line = br.readLine();
		if (line.matches(patternDateTime5)) {
		   final LogRecordV1 unit = super.convert(line);
		   result.add(unit);
		}else{
		    if(line.matches(patternDateTime6)){
			sbf = new StringBuffer();
			sbf.append(line).append("\n");
		    }else  if(line.matches(patternDateTime7)){
			sbf.append(line).append("\n");
			final LogRecordV1 unit = super.convert(sbf.toString());
			result.add(unit);
		    }else{
			sbf.append(line).append("\n");
		    }
		}
	    }
	} catch (IOException e) {
	    e.printStackTrace();
	} finally {
	    if (br != null) {
		try {
		    br.close();
		} catch (IOException e) {
		    e.printStackTrace();
		}
	    }
	}
	final Comparator<? super LogRecordV1> comparator= new LogRecordV1SortByTime()  ;
	
	Collections.sort(result, comparator);
	
	logger.debug("Time (seconds) taken  by readLogsBySortTime is  {}", String.valueOf((System.nanoTime() - logicProcessStartTime) / 1.0e9));
	return result;
    }
    
    /***
     * 將原有資料依時間進行重新排序
     * **/
    public void accountSortByTime(final LogicProcess logic ,final File srcDir ){
	File[] listFiles = srcDir.listFiles();
	for(final File file  : listFiles){
	     final List<LogRecordV1> unitList = readLogsBySortTime(file);
	     logic.processList(unitList);
	}
    }
    /***
     * 依照不同的帳號分離log資料檔進行儲存
     * **/
    public void readLogsV3(final LogicProcess logic ,final File... srcFiles){
	long logicProcessStartTime = System.nanoTime();	
	final SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
	
	if(ArrayUtils.isNotEmpty(srcFiles)){
	    for(File srcFile : srcFiles  ){
		BufferedReader br =null;
		try {
		    final Date standardDate = sdf.parse("2014/04/30");
		    if (srcFile.exists() && srcFile.getName().contains(".gz")) {
			final Path srcPath = srcFile.toPath();

			final GZIPInputStream gzip = new GZIPInputStream(Files.newInputStream(srcPath, StandardOpenOption.READ));
			br = new BufferedReader(new InputStreamReader(gzip));

		    } else {
			br = new BufferedReader(new FileReader(srcFile));
		    }
		    StringBuffer sbf = null;
		    while (br.ready()) {
			String line = br.readLine();
			if (line.matches(patternDateTime5)) {
			   final LogRecordV1 unit = super.convert(line);
			   final   Date date = unit.getTime();
			   if(date.getTime()>standardDate.getTime()){
			       logic.process(unit);
			   }
			   
			}else{
			    if(line.matches(patternDateTime6)){
				sbf = new StringBuffer();
				sbf.append(line).append("\n");
			    }else  if(line.matches(patternDateTime7)){
				sbf.append(line).append("\n");
				final LogRecordV1 unit = super.convert(sbf.toString());
				final   Date date = unit.getTime();
				if (date.getTime() > standardDate.getTime()) {
				    logic.process(unit);
				}
			    }else{
				sbf.append(line).append("\n");
			    }
			}
		    }
		} catch (IOException e) {
		    e.printStackTrace();
		} catch (ParseException e) {
		    e.printStackTrace();
		} finally {
		    if (br != null) {
			try {
			    br.close();
			} catch (IOException e) {
			    e.printStackTrace();
			}
		    }
		}
	    }
	}
	logger.debug("Time (seconds) taken  by reagLogs is  {}", String.valueOf((System.nanoTime() - logicProcessStartTime) / 1.0e9));
    }
    protected List<String> readLines(final File srcFile){
	List<String> result=null;
	BufferedReader br =null;
	
	try {
	    if(srcFile.exists() && srcFile.getName().contains(".gz")){
		result=new ArrayList<String>();
		final	Path srcPath = srcFile.toPath();
		
		final GZIPInputStream gzip = new GZIPInputStream(Files.newInputStream(srcPath, StandardOpenOption.READ));		
		br = new BufferedReader(new InputStreamReader(gzip));
		while(br.ready()){
		    result.add(br.readLine());
		}
		
	    }else{
		final	Path srcPath = srcFile.toPath();
		Charset cs=Charset.forName("UTF-8");		
		result = Files.readAllLines(srcPath, cs);
		
	    }
	   
	} catch (IOException e) {
	    e.printStackTrace();
	} finally {
	    if (br != null) {
		try {
		    br.close();
		} catch (IOException e) {
		    e.printStackTrace();
		}
	    }
	}
	return result;
    }
    public void anayLizingUnit( final File file,final String outFilePath){
	final List<LogRecordV1> originallist = readLogsV2(file);
	String search001="(tw.gov.moi.security.AuthorityFilter:225)";
	String search002="(tw.gov.moi.security.AuthorityFilter:160)";
	String search003="(tw.gov.moi.security.AuthorityFilter:195)";
	String search004="Profiler [AuthorityFilter]";
	final 	String patternMethodRegExr="\\s*\\-*\\+\\s*Profiler\\s{1}\\[AOP\\s{1}(.*?)\\].*";
	final	Pattern patternMethod = Pattern.compile(patternMethodRegExr);
	final 	String patternSpendTimeExr="-*\\s*Total\\s*.*\\[AuthorityFilter\\]\\s*(.*?)milliseconds.";
	final	Pattern patternSpendTime = Pattern.compile(patternSpendTimeExr);
	
	
	final	Pattern patternStandard = Pattern.compile(pattern);
	final List<String> sampleList =new ArrayList<String>();
	for(final LogRecordV1 unit: originallist){
	    String srcline =unit.getSrc();
	    if(StringUtils.contains(srcline, search001)
		    || StringUtils.contains(srcline, search002)
		    ||StringUtils.contains(srcline, search003)
		     ||StringUtils.contains(srcline, search004)
		    ){
		if(StringUtils.contains(srcline, search004)){
		    final  Matcher matcher = patternStandard.matcher(srcline);
		    matcher.find();
		    String data =matcher.group(0);
		    StringBuffer sbf =new StringBuffer();
		    sbf.append(data);
		    final	String msg = unit.getMessage();
		    final 	String[] lines = StringUtils.splitPreserveAllTokens(msg,"\n");
		    for (String line : lines) {
			final Matcher matcherMethod = patternMethod.matcher(line);
			final Matcher matcherSpendTime = patternSpendTime.matcher(line);
			if (matcherMethod.find()) {
			    int groupCount = matcherMethod.groupCount();
			    if (groupCount >= 1) {
				String methodName = matcherMethod.group(1);
				sbf.append(" method: ").append(methodName);
//				result.setMethodName(methodName);
			    }
			}
			if (matcherSpendTime.find()) {
			    int groupCount = matcherSpendTime.groupCount();
			    if (groupCount >= 1) {
				String spendTime = matcherSpendTime.group(1);
				float spendMilliseconds = NumberUtils.toFloat(StringUtils.trim(spendTime));
				sbf.append(" spendMilliseconds: ").append(spendMilliseconds);
//				result.setSpendMilliseconds(spendMilliseconds);
			    }
			}
		    }
		    sbf.append(search003);
		    sampleList.add(sbf.toString()); 
		}else{
		    sampleList.add(srcline); 
		}
		
	    }
	}
	final Charset cs = Charset.forName("UTF-8");
	Path outPath =FileSystems.getDefault().getPath(outFilePath, file.getName()+"_simpale");
	try {
	    Files.write(outPath, sampleList, cs, StandardOpenOption.CREATE_NEW);
	} catch (IOException e) {
	    e.printStackTrace();
	}
    }
    public List<String> anayLizingUnitSequence01(final File file) throws IOException{
	final List<String> forPrint =new ArrayList<String>();
	final SimpleDateFormat viewsdf = new SimpleDateFormat("mm:ss.SSS");
	final SimpleDateFormat sdf = new SimpleDateFormat("MM/dd-HH:mm:ss.SSS");
	final List<String> lines = FileUtils.readLines(file);
	final List<LogRecordV1> list = new ArrayList<LogRecordV1>();
	for(String line:lines){
	    final LogRecordV1 result = new LogRecordV1();
	    final String[] strArray = StringUtils.splitPreserveAllTokens(line, "|");
	  
	    try {
		final Date time = sdf.parse(strArray[0]);
		result.setTime(DateUtils.setYears(time, 2014));
	    } catch (ParseException e) {
		e.printStackTrace();
	    }
	    result.setTaskName(strArray[1]);
	    result.setMessage(strArray[2]);
	    list.add(result);
	}
	Date startTime = null; 
	String startTaskName =null;
	Date endTime = null;
	String endTaskName =null;
	for (int i = 0; i < list.size(); ++i) {
	    int r = i % 2;
	    LogRecordV1 unit = list.get(i);
	    final  String msg = unit.getMessage();
	    switch (r) {
	    case 0:
		if(!StringUtils.contains(msg, "initOperationDTO")){
		    list.remove(i);
		    --i;
		}else{
		    startTime = unit.getTime();
		    startTaskName =unit.getTaskName();
		}
		break;
	    case 1:
		if(!StringUtils.contains(msg, "saveXldf")){
		    list.remove(i);
		    --i;
		}else{
		    endTime= unit.getTime(); 
		    endTaskName =unit.getTaskName();
		    if(StringUtils.equalsIgnoreCase(startTaskName, endTaskName)){
			final	Date time = new Date(endTime.getTime()-startTime.getTime());
			forPrint.add(sdf.format(startTime)+" | "+endTaskName+" | "+viewsdf.format(time));
		    }
		}
		break;
	    default:
		break;
	    }
	}
	return forPrint;
    }
    /**
     * 初步分析(針對debug level 將initOperationDTO,saveXLDF挑出來)
     * **/
    public void analyzingUnitV3(final String outFilePath ,final File[] srcFiles){
	for(final File srcFile : srcFiles){
	    anayLizingUnitV2(srcFile, outFilePath);
	}
    }
    public void anayLizingUnitV2( final File file,final String outFilePath){
	String[] tasks ={"RL01210","RL01220","RL01230","RL01240","RL01250","RL01263","RL01273","RL01290","RL012B0","RL012E0","RL012F0","RL012H0","RL01310","RL01320","RL01330","RL01340","RL01390",
		"RL02100","RL02200","RL02300","RL02510","RL02800","RL02A10","RL02D00","RL02E00","RL02F00"
	};
	
   	final List<LogRecordV1> originallist = readLogsV2(file);
   	String search004="Profiler [AuthorityFilter]";
   	final 	String patternMethodRegExr="\\s*\\-*\\+\\s*Profiler\\s{1}\\[AOP\\s{1}(.*?)\\].*";
   	final	Pattern patternMethod = Pattern.compile(patternMethodRegExr);
   	
   	
   	final	Pattern patternStandard = Pattern.compile(pattern);
   	final List<String> sampleList =new ArrayList<String>();
   	for(final LogRecordV1 unit: originallist){
   	    String taskName = StringUtils.trim(unit.getTaskName());
   	    String srcline =unit.getSrc();
   	    if(StringUtils.contains(srcline, search004) && ArrayUtils.contains(tasks, taskName)
   		    ){
   		   boolean needToCaculate=false;
		    final  Matcher matcher = patternStandard.matcher(srcline);
		    matcher.find();
		    String data =matcher.group(0);
		    StringBuffer sbf =new StringBuffer();
		    sbf.append(data).append("|").append(taskName).append("|");
		    final	String msg = unit.getMessage();
		    final 	String[] lines = StringUtils.splitPreserveAllTokens(msg,"\n");
		    for (String line : lines) {
			final Matcher matcherMethod = patternMethod.matcher(line);
			if (matcherMethod.find()) {
			    int groupCount = matcherMethod.groupCount();
			    if (groupCount >= 1) {
				String methodName = matcherMethod.group(1);
				if(methodName.contains("initOperationDTO")||methodName.contains("saveXldf")){
				    sbf.append(methodName);
				    needToCaculate=true;
				}
				
			    }
			}
		    }
//		    sbf.append(search003);
		    if(needToCaculate){
			sampleList.add(sbf.toString()); 
		    }
		    
		
   	    }
   	}
   	final Charset cs = Charset.forName("UTF-8");
   	Path outPath =FileSystems.getDefault().getPath(outFilePath, file.getName()+"_simpale");
   	File dir = new File(outFilePath);
   	Path path = dir.toPath();
   	
	try {
	    if (Files.notExists(path)) {
		FileUtils.forceMkdir(dir);
//		Files.createDirectory(path);
	    }
	    Files.write(outPath, sampleList, cs, StandardOpenOption.CREATE_NEW);
	} catch (IOException e) {
   	    e.printStackTrace();
   	}
       }
}
